package com.world.rickcloudendpoint.basic.netty;

import com.world.rickcloudendpoint.basic.AbstractNetServerService;
import com.world.rickcloudendpoint.common.ThreadNameFactory;
import com.world.rickcloudendpoint.common.constant.GlobalConstants;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.log4j.Log4j2;

/**
 * @description: tcp协议通讯的服务器服务
 * @author: jlsong
 * @date: 2021/12/22 21:12:23
 * @version: 1.0
 */
@Log4j2
public abstract class AbstractNetTcpServerService extends AbstractNetServerService {
    /**
     * boss 线程组用于处理连接工作
     */
    private EventLoopGroup boss;
    /**
     * work 线程组用于数据处理
     */
    private EventLoopGroup worker;

    private final ThreadNameFactory bossThreadNameFactory;
    private final ThreadNameFactory workerThreadNameFactory;
    private final ChannelInitializer channelInitializer;

    private ChannelFuture serverChannelFuture;


    public AbstractNetTcpServerService(String serviceId, int serverPort,
                                       String bossThreadName, String workerThreadName,
                                       ChannelInitializer channelInitializer) {
        super(serviceId, serverPort);
        this.bossThreadNameFactory = new ThreadNameFactory(bossThreadName);
        this.workerThreadNameFactory = new ThreadNameFactory(workerThreadName);
        this.channelInitializer = channelInitializer;
    }


    @Override
    public void startup() throws Exception {
        boss = new NioEventLoopGroup(1, bossThreadNameFactory);
        worker = new NioEventLoopGroup(0, workerThreadNameFactory);
        try {
            log.info(String.format("%s start up tcp reception server ...", GlobalConstants.TCP_RECEPTION_LOG_ID));
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, worker);
            // 非阻塞socket
            bootstrap.channel(NioServerSocketChannel.class)
                    //服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    // 重用地址
                    .childOption(ChannelOption.SO_REUSEADDR, true)
                    // 最大接收容量
                    .childOption(ChannelOption.SO_RCVBUF, 65536)
                    .childOption(ChannelOption.SO_SNDBUF, 65536)
                    //将小的数据包包装成更大的帧进行传送，提高网络的负载
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    //设置TCP长连接,一般如果两个小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.ALLOCATOR, new PooledByteBufAllocator(false))  // heap buf 's better
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(channelInitializer);

            // 绑定端口，开始接收进来的连接
            serverChannelFuture = bootstrap.bind(serverPort).sync();
            if (serverChannelFuture.isSuccess()) {
                log.info(String.format("%s start up tcp reception  server success,listener to port->%s ", GlobalConstants.TCP_RECEPTION_LOG_ID, serverPort));
            }

            //TODO这里会阻塞main线程，暂时先注释掉
            // 等待服务器socket关闭
            serverChannelFuture.channel().closeFuture().sync();
//            serverChannelFuture.channel().closeFuture().addListener(ChannelFutureListener.CLOSE);
        } catch (Exception e) {
            log.error(String.format("%s start up tcp reception  server failed,the reason is %s ", GlobalConstants.TCP_RECEPTION_LOG_ID, e.getMessage()), e);
        } finally {
            shutdown();
        }

    }

    @Override
    public void shutdown() throws Exception {
        try {
            if (boss != null) {
                boss.shutdownGracefully().sync();
            }
            if (worker != null) {
                worker.shutdownGracefully().sync();
            }
            log.info(String.format("%s shut down tcp reception  server  success ", GlobalConstants.TCP_RECEPTION_LOG_ID));
        } catch (Exception e) {
            log.error(String.format("%s shut down tcp reception  server failed,the reason is %s ", GlobalConstants.TCP_RECEPTION_LOG_ID, e.getMessage()), e);
        }
    }
}
